Skip to content

Dark mode rework#2

Open
aniro wants to merge 6 commits into
masterfrom
dark-mode-rework
Open

Dark mode rework#2
aniro wants to merge 6 commits into
masterfrom
dark-mode-rework

Conversation

@aniro
Copy link
Copy Markdown
Owner

@aniro aniro commented May 4, 2026

Disclaimer: This pull request was authored by Claude (Anthropic's LLM). All commits, design decisions, and code were generated by the model. A human reviewed and accepted the changes interactively.

Summary

Adds dark mode to NotepadNext with a tristate theme setting (System / Light / Dark, default System). When set to System, the application follows the desktop environment's color scheme via QStyleHints::colorScheme() and updates live when the system preference changes. Light and Dark are explicit overrides for users who want their editor's theme independent of the rest of the desktop.

Design

A single ApplicationSettings::theme enum drives everything. effectiveDarkMode() resolves System against the current QStyleHints::colorScheme(). A single effectiveDarkModeChanged(bool) signal fires on either user-initiated theme changes or system color-scheme changes (when in System mode), and every consumer connects to that one signal.

Theming layers:

  1. Qt widgets. MainWindow::applyStyleSheet() switches between Qt's adaptive standardPalette() (System mode) and explicit Fusion palettes (Light / Dark mode). Targeted CSS in stylesheets/npp.css (light) and the new stylesheets/npp-dark.css (dark) covers ADS dock tabs, status bar, and QuickFindWidget. No third-party stylesheet dependency.

  2. Scintilla editor surfaces. EditorManager::applyEditorTheme() sets fold markers, element colors (caret line, selection inactive, whitespace, fold line), fold margin colors, and STYLE_DEFAULT. A separate applyEditorNamedStyles() sets STYLE_LINENUMBER, STYLE_BRACELIGHT, STYLE_BRACEBAD, STYLE_INDENTGUIDE. The split is needed because Lua's SetStyle calls StyleClearAll on every language load, which would otherwise wipe these named styles.

  3. Syntax highlighting. init.lua builds a theme table from a dark_mode global injected by C++. SetStyle() writes STYLE_DEFAULT from the theme, calls StyleClearAll, then auto-translates canonical light-mode colors per language style: style.fgColor == light_fg → theme.default_fg and style.bgColor == light_bg → theme.default_bg. The vast majority of language .lua files specify black on white and get auto-converted; deliberate syntax colors (blue keywords, green comments, gray strings, orange numbers) pass through unchanged and remain readable on a dark background. No language definition files were edited.

  4. LuaConsoleDock. Now takes ApplicationSettings*, applies theme-aware Lua lexer styles, and reapplies them on effectiveDarkModeChanged.

  5. Preferences UI. New "Theme" combo box (Follow system / Light / Dark) replacing the original boolean checkbox idea.

Files

Area Change
ApplicationSettings.{h,cpp} Theme enum setting + effectiveDarkMode() helper + effectiveDarkModeChanged signal; subscribes to QStyleHints::colorSchemeChanged
EditorManager.{h,cpp} applyEditorTheme() + applyEditorNamedStyles(); signal connection
NotepadNextApplication.{h,cpp} dark_mode Lua injection; refreshEditorTheme() slot; applyEditorNamedStyles after Lua language load
scripts/init.lua UpdateTheme(), theme table, SetStyle() rewrite with auto-translation
dialogs/MainWindow.cpp applyStyleSheet() rewritten for tristate; signal connection
dialogs/PreferencesDialog.{ui,cpp} Theme combo box + wiring
docks/LuaConsoleDock.{h,cpp} Theme-aware lexer styles, signal connection
stylesheets/npp-dark.css (new) Dark variants for ADS tabs and QuickFindWidget
resources.qrc Register npp-dark.css

Verification

  • Clean build against Qt 6.10 and the master CMake setup.
  • System theme with GNOME prefer-dark: app starts dark; switching GNOME to light makes the app re-theme live.
  • Theme=Light explicit: editor white, line numbers gray-on-#E4E4E4, menu/toolbar/tabs all light.
  • Theme=Dark explicit: editor #1E1E1E, line numbers #858585-on-#252526, full Qt widget palette dark.
  • Opening a C/C++ file in dark mode: keywords blue, strings gray, comments green, numbers orange. All readable on dark background. No edits to language definition files.
  • The setting is persisted in ~/.config/NotepadNext/NotepadNext.ini under [Gui]/Theme (0 = System, 1 = Light, 2 = Dark).

Test Plan

  • Open Preferences → Theme combo, switch between Follow system / Light / Dark; confirm the editor and Qt chrome update without restart.
  • With Theme=Follow system, toggle the desktop's dark/light preference (GNOME Settings → Appearance, or gsettings set org.gnome.desktop.interface color-scheme prefer-dark/default); confirm the app re-themes live.
  • Open a few language files (C, Python, JSON, etc.) in both modes; confirm syntax highlighting remains readable.
  • Open the Lua console dock and confirm its theme matches.

aniro-s added 6 commits May 4, 2026 19:48
Implements a clean dark mode that drives all theming from a single ApplicationSettings::darkMode boolean. Live toggle: no restart required.

The Lua side defines a `theme` table that is rebuilt from a `dark_mode` global injected by C++. SetStyle() sets STYLE_DEFAULT from theme and calls StyleClearAll, then auto-translates canonical light-mode colors (black text, white background) to theme equivalents per-style. Language `.lua` files therefore work in either mode without modification: the vast majority specify black on white, which gets converted, while deliberate syntax colors (blue keywords, green comments, etc.) pass through unchanged.

EditorManager::applyEditorTheme() handles all Scintilla UI surfaces (margins, caret line, fold markers, line numbers, brace match) for both modes. NotepadNextApplication::refreshEditorTheme() updates the Lua theme and re-applies styles to every open editor when the setting changes.

For Qt widgets, MainWindow::applyStyleSheet() switches between Fusion-based light and dark QPalettes and loads either npp.css or the new npp-dark.css for ADS dock tabs and the QuickFindWidget. No third-party stylesheet dependency.

LuaConsoleDock takes ApplicationSettings and reapplies its lexer styles on darkModeChanged so the console's Lua syntax highlighting tracks the theme.

A "Dark mode" checkbox is added to the GUI section of Preferences.
Lua SetStyle() calls StyleClearAll on language load, which resets STYLE_LINENUMBER, STYLE_BRACELIGHT, STYLE_BRACEBAD, and STYLE_INDENTGUIDE to STYLE_DEFAULT values, wiping the colors set by applyEditorTheme.

Split applyEditorTheme into two methods: the full theme (including STYLE_DEFAULT + StyleClearAll) and a separate applyEditorNamedStyles for the colors that need re-applying after each language load. NotepadNextApplication::setEditorLanguage calls applyEditorNamedStyles after the Lua SetLanguage call so line numbers and brace match retain their themed colors regardless of which language is active.
Qt 6.5+ has Fusion::standardPalette() honor QStyleHints::colorScheme(), so on systems where the user has selected dark mode at the OS level (GNOME, KDE, etc.) Fusion::standardPalette() returns dark colors. Setting that as the application palette in light mode left menu bar, toolbar, and dock tabs dark while only the editor and stylesheet-targeted widgets switched to light.

Define an explicit light palette parallel to the existing dark one so the palette is fully driven by the user-selected DarkMode setting and ignores the system color scheme. Verified visually: menu bar, toolbar, and ADS tabs render light when DarkMode=false and dark when DarkMode=true, regardless of the GNOME theme.
Replace the DarkMode boolean with a Theme enum (System / Light / Dark, default System). System mode reads QGuiApplication::styleHints()->colorScheme() and listens for colorSchemeChanged so the app follows the desktop environment's light/dark preference, including live changes (e.g. GNOME night-mode auto-switching at sunset). Light and Dark remain explicit overrides for users who want their editor independent of the desktop theme.

ApplicationSettings exposes a single effectiveDarkMode() helper plus an effectiveDarkModeChanged(bool) signal that fires on either theme change or system color-scheme change while in System mode. All consumers (EditorManager, NotepadNextApplication, MainWindow, LuaConsoleDock) call effectiveDarkMode() and connect to that signal, so the resolution-to-dark/light logic stays in one place.

Preferences gets a "Theme" combo box with three options replacing the previous checkbox.
In System theme mode, applyStyleSheet now applies QApplication::style()->standardPalette() instead of our hardcoded Fusion palette. Qt 6.5+ Fusion already adapts standardPalette to QStyleHints::colorScheme(), so this lets the Qt widgets follow the desktop environment's palette rather than imposing our own interpretation of "dark" or "light".

Light and Dark explicit modes still use the hardcoded palettes since the user has chosen to override the system, and Qt's standardPalette would leak through the system color scheme there.

Scintilla editor surfaces still resolve through effectiveDarkMode() since they cannot read the Qt palette directly.
Stop forcing QApplication::setStyle(Fusion) globally and skip QApplication::setPalette() entirely when the user has selected System theme. Qt 6.5+ already syncs the palette with QStyleHints::colorScheme() and the platform integration picks the right style on its own; re-applying both on every applyStyleSheet call only re-emits paletteChange events and overrides legitimate platform choices.

Light and Dark explicit modes still apply their hardcoded Fusion-derived palettes since the user has chosen to override the system there.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants